import { Inject, Provide } from '@midwayjs/decorator';
import { JwtService } from '@midwayjs/jwt';
import { Context } from '@midwayjs/koa'
import privacy from '../privacy';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { UserEntity } from '../entity/user';
import { Repository } from 'typeorm';
import axios from 'axios';
import { RedisService } from '@midwayjs/redis';
import { FoodEntity } from '../entity/food';
import { OrderEntity } from '../entity/order';
import { AddressDTO } from '../dto/address';
import { UseraddressEntity } from '../entity/address';
import { MapDTO } from '../dto/map';
import { OrderitemEntity } from '../entity/Orderitem';
import { OSSService } from '@midwayjs/oss';
const OSS = require('ali-oss')
const fs = require("fs")
const path = require("path")

var crypto = require("crypto");
@Provide()
export class UserService {
  @Inject()
  ctx: Context;

  @Inject()
  OSSService:OSSService;

  @Inject()
  jwtService: JwtService;

  @Inject()
  redisService: RedisService;

  @InjectEntityModel(UserEntity)
  UserModel: Repository<UserEntity>;
  
  @InjectEntityModel(FoodEntity)
  FoodModel: Repository<FoodEntity>;
  @InjectEntityModel(OrderEntity)
  OrderModel: Repository<OrderEntity>;
  @InjectEntityModel(OrderitemEntity)
  OrderitemEntity: Repository<OrderitemEntity>;
  @InjectEntityModel(UseraddressEntity)
  UseraddressModel: Repository<UseraddressEntity>;

  async updateavatar(options: { nickName: string, avatarUrl: string }) {

    var userinfo = await this.getuserinfo()
    console.log("用户信息", userinfo);

    //  将头像跟昵称 更新到数据库 中
    await this.UserModel.createQueryBuilder()
      .update(UserEntity)
      .set({ avatar: options.avatarUrl, name: options.nickName })
      .where({ id: userinfo.id })
      .execute();


    //  两种方式 v2  v3



    return {
      code: 20000,
      message: "修改成功"
    }

  }



  /**
   * 
   * @param code 提交订单
   * @returns 
   */
  // 不会专门将数据插入数据库中
  async commitOrder(body: any) {
    //  计算价格  价格不是前端计算的

    console.log("body", body.order);

    //  创建订单 查看价格
    //  如何计算价格  
    //  查询id 6 7 12 13的价格
    let find = await this.FoodModel.createQueryBuilder('foods')
      .leftJoinAndSelect("foods.spu", "spu")
      .leftJoinAndSelect("spu.sku", "sku")
    // .orWhere({ id: 6 })
    // .orWhere({ id: 7 })
    // .orWhere({ id: 12 })
    // .orWhere({ id: 13 })
    // .getMany()
    body.order.forEach(v => {
      find.orWhere({ id: v.id })
    })
    let result = await find.getMany()
    var totalprce = 0;

    // console.log(result);
    body.order.forEach((v, i) => {

      if (v.select.length == 0) {
        totalprce = totalprce + result[i].price * v.num
      } else {
        v.select.forEach((e, x) => {
          totalprce = totalprce + Number(result[i].spu[x].sku.find(z => z.id == e).price) * v.num
        })
      }
    })

    console.log("总价格", totalprce);
    return {
      code: 20000,
      data: {
        result,
        totalprce
      }
    }
  }

  //  redis  => 接受短信功能   随机号码存在服务器 input 输入后 发送给后端校验是否一致 
  //   数据库实现一个 数据有自动消失  2个小时后 消失  不占内容  数据库
  //  接口 =>  发送过去 就存储一个数据 
  //  接口2  => 查看这个数据   1分钟后查询是否还存在 



  async createOrder(body: any) {

    console.log(body);

    //  调用内部的函数 进行 商品的价格计算 
    let { data } = await this.commitOrder(body)
    let { result, totalprce: totalprice } = data
    console.log(result, totalprice);
    console.log(result[0].des);

    let { openid } = await this.getuserinfo()

    let ordernum = await this.creadOrder(15)
    let address = await this.UseraddressModel.findOne({ where: { id: body.address } })
    console.log(address);
    var str = address.address + address.detail + address.name + address.mobile
    let { longitude, latitude } = address
    let result2 = await this.OrderModel.createQueryBuilder().insert().values({
      orderid: ordernum,
      totalPrice: totalprice,
      status: 0,
      address: str,
      longitude,
      latitude,
      shop: body.shop
    }).execute();
    //  得到id 是为了后续给他 的儿子认父亲  儿子是商品
    console.log("插入后的结果", result2.identifiers[0].id);


    console.log("儿子", result);
    var arr = result.map((v, index) => {
      var str = ""
      v.spu.forEach(v => {
        // v  分别是 分量      任意口味
        //  v.sku  是 分量 =>  小份  大份 标准分  
        v.sku.forEach(z => {
          str += z.name
        })
      })

      return {
        pid: result2.identifiers[0].id,
        price: v.price + "",
        num: body.order[index].num,
        url: v.url,
        des: v.des,
        sku: str
      }
    })
    await this.OrderitemEntity.createQueryBuilder().insert().values(arr).execute()






    //  得到价格后 要想微信创建 支付的 参数 


    return {
      code: 20000,
      order: ordernum,
      ...await this.pay({ price: totalprice, des: result[0].des, openid: openid, ordernum })
    }

  }

  async getfriends(){
    //  获取用户信息
        //  获取用户 id  
        let { id } = await this.getuserinfo()

        return { 
          id
        }


  }

  async addfriends(){
    //  获取用户信息
        //  获取用户 id  
        let { id } = await this.getuserinfo()

        return { 
          id
        }


  }

  async deletefriends(){
    //  获取用户信息
        //  获取用户 id  
        let { id } = await this.getuserinfo()

        return { 
          id
        }


  }


  async getOss() {

    const date = new Date();
    date.setDate(date.getDate() + 1);
    const policy = {
        expiration: date.toISOString(), //设置Unix时间戳（自UTC时间1970年01月01号开始的秒数），用于标识该请求的超时时间。
        conditions: [
          ["content-length-range", 0, 1048576000], //设置上传文件的大小限制。      
        ],
      };
      const formData   = this.OSSService.calculatePostSignature(policy)
    return  {
         code:20000,
         message:"成功",
         data:{
            ...formData,
            host:privacy.host
         }
    }
  }
  async deleteoss(pathurl) {

    console.log("删除的路径",pathurl);
    
  if(!pathurl){
    return
  }
  const client = new OSS({
    // yourregion填写Bucket所在地域。以华东1（杭州）为例，Region填写为oss-cn-hangzhou。
    region: 'oss-us-east-1',
    //  阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
    accessKeyId: privacy.accessKeyId,
    accessKeySecret: privacy.accessKeySecret,
    // endpoint:selfconfig.endpoint,
    // 填写Bucket名称。
    bucket: privacy.bucket,
  });

client.useBucket(privacy.bucket);
pathurl = pathurl.replace(privacy.host,"")
console.log(pathurl);

   let  result = await client.delete(pathurl);
   console.log(result);
   
      return { 
         message:"成功"
      }
}

 async friend(){
   let {  id  }  =await this.getuserinfo()
   let result  =await this.UserModel.createQueryBuilder("user").where("user.id != :id ", {  id }).getMany()
    return { 
       code:2000,
       list : result
    }
  }

  async getdetailOrder(body: any) {
    //  body 是订单号 
    console.log("oderid1111111111111", body.orderid);

    let result = await this.OrderModel.createQueryBuilder("order")
      .leftJoinAndSelect("shop_entity", "shop", "shop.id =order.shop ")
      .leftJoinAndSelect("order.foodes", "foodes")
      //  这次连表在数据库是没有实体关系 OneTomay
      //  连接表名为  shop_entity 取名为 shop   当 shop 的id  跟 order中的shop 相等 连接
      .where({ orderid: body.orderid })
      .getOne()
    console.log("联查结果", result);



    return {
      code: 20000,
      data: {
        result
      }
    }
  }



  async gettell(code: string) {

    // reids
    let token = await this.redisService.get('access_token')
    // console.log("token", token);

    // code  发生请求
    let { data } = await axios({
      url: "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + token
      ,
      method: "post",
      data: {
        code: code
      }
    })


    console.log(data.phone_info.phoneNumber);
    //  操作数据库 存进去   存给谁? 
    //  要知道对方的身份
    let user = await this.getuserinfo()
    //  获取用户 身份  整体框架直接用
    console.log(" 用户身份", user.id);

    await this.UserModel.createQueryBuilder().update()
      .set({ phone: data.phone_info.phoneNumber })
      .where({ id: user.id })
      .execute();
    return {
      data
    }



  }


  /**
   * 提交订单
   */
  async commitoder(option: any[]) {
    console.log("订单来了", option);
    //  查询 传过来id 的价格
    //   id 11
    // let result = await this.FoodModel.createQueryBuilder().where({ id: 11 }).orWhere({ id: 8 }).getMany()
    //  将语法拆分出来
    let find = this.FoodModel.createQueryBuilder()
    option.forEach((v, i) => {
      // if (i == 0) {
      //   find.where({ id: v.id })
      // } else {
      find.orWhere({ id: v.id })
      // }

    })


    let result = await find.getMany()
    //  拿到真实的数据价格了

    var total = 0
    //  计算总价格

    option.forEach(v => {
      var obj = result.find(e => e.id == v.id)
      total = total + obj.price * v.num
    })

    console.log(total);
    let user = await this.getuserinfo()
    //  创建一个订单号  取餐码  A 用户 B 用户下单   42  13  
    //  01  02  03  
    let order = new OrderEntity()

    order.orderid = await this.creadOrder(15) + ""
    order.totalPrice = total
    order.status = 0
    order.goods = JSON.stringify(option)

    //  不行查询查询数据库 
    let str: string = await this.redisService.get("ordercode")

    var code: string;
    if (!str) {
      code = "1"
    } else {
      code = (Number(str) + 1) + ""
    }
    await this.redisService.set("ordercode", code)
    order.code = code
    this.OrderModel.save(order)
    //  今天买 10块钱一份

    //   今天卖 9999块一份

    //   当天退款 只退 id8   一份

    return {
      code: 20000,
      odercode: code,
      ...await this.pay({ price: total, des: "云饺子", openid: user.openid, ordernum: order.orderid })
    }


  }



  /**
   *  提交收货地址
   */
  async addaddress(options: AddressDTO) {
    console.log("收货地址", options);

    //  获取用户 id  
    let { id } = await this.getuserinfo()




    let { longitude, latitude, name, city, province, areaId, address, mobile, area } = options
    console.log("用户id", id);
    await this.UseraddressModel.createQueryBuilder()
      .insert()
      .values({ longitude, latitude, name, city, province, areaId: Number(areaId), detail: address, address: province + city + area, mobile, userId: id, area })
      .execute();
    return {
      code: 20000,
      success: "ok"
    }
  }



  /**
   * 获取收货地址
   */
  async getaddress(option: MapDTO) {
    //  识别是谁的 id  
    // let { id } = await this.getuserinfo()

    //  开始查询
    //  通过定位的距离来进行查询
    // select 选择查询的 字段  
    //  使用mysql 内置的函数   POINT   返回  数据库中   两个点之间距离   我们填入两个点之后,  该函数会返回 该数据两点的距离 的数据 , 并这个数据定义  distance
    let result = await this.UseraddressModel.createQueryBuilder()
      .select(['id', 'name', "isDefault", "address", "mobile", "cityId", "areaId", "city", "province", "area", "longitude",
        "latitude"
      ])
      .addSelect(`st_distance_sphere(POINT(${option.longitude},${option.latitude}), POINT(longitude, latitude))`, 'distance')
      .orderBy('distance')
      .getRawMany();



    return {
      code: 20000,
      result
    }
  }



  /**
   * 更新收货地址
   */
  async updataaddress(option: AddressDTO) {

    console.log(option);
    let { id, areaId, distance, menpai, ...obj } = option
    this.UseraddressModel.createQueryBuilder()
      .update()
      .set({ ...obj, detail: menpai, areaId: Number(areaId) })
      .where("id = :id", { id: option.id })
      .execute();
    return {
      code: 20000,
      message: "成功"
    }



  }


  /**
   * 获取用户信息  解析token 获得用户头像
   */
  async getuserinfo() {
    //  解析token  返回用户信息
    let result: any = {}

    let token = this.ctx.request.header.token || this.ctx.request.header.authorization.split(" ")[1]
    console.log("token",token);
    
    result = this.jwtService.verifySync(token + "", { complete: true })
    console.log("解析结果", result);
    
    return result.payload
  }


  /**
   * v3 支付
   */
  async pay(option: { price: number, des: string, openid: string, ordernum: string }) {

    var randstr = await this.getNonceStr(32)
    let obj = {
      method: "POST",
      url: "/v3/pay/transactions/jsapi",
      timeStamp: parseInt((Date.now() / 1000) + ""),
      ranstr: randstr,
      body: {
        appid: privacy.appid,
        mchid: privacy.mch_id + "",
        description: option.des,
        out_trade_no: option.ordernum + "", // 内部 唯一的订单号  由 时间 +id + 随机数组成
        // out_trade_no: await this.creadOrder(15) + "", // 内部 唯一的订单号  由 时间 +id + 随机数组成
        notify_url: "https://md.jinghuanqiu.com/notify/wxnotify", // 异步通知地址 
        //  当我们唤起了支付 , 对方有没有支付 我们是不知道
        //  如果对方付款成功后,  微信是知道 ,但是我们不知道   微信会发生一个api 给我们   告诉我们  某某openid  已经付款了 订单号多少
        amount: {
          "total": option.price,
          "currency": "CNY"
        },
        payer: {
          openid: option.openid
        },
      }
    }

    var sign = await this.createSignv3(obj)
    let { data } = await axios({
      url: "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",
      method: "post",
      timeout: 30000,
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": `WECHATPAY2-SHA256-RSA2048 mchid="${privacy.mch_id}",nonce_str="${obj.ranstr}",signature="${sign}",timestamp="${obj.timeStamp}",serial_no="${privacy.pay_id}"`
      },
      data: obj.body
    })

    // "prepay_id": "wx0219370430661268e058d66a114b5f0000" 
    //  如何用来支付? 

    // console.log("data", obj.ranstr);

    var pack = `prepay_id=${data.prepay_id}`
    var str2 = `${privacy.appid}\n${obj.timeStamp}\n${obj.ranstr}\n${pack}\n`
    var sing2 = await this.createSignv3str(str2)

    // console.log(obj);

    return {
      data: {
        timeStamp: obj.timeStamp,
        nonceStr: obj.ranstr,
        package: pack,
        paySign: sing2
      }
    }
  }


  /**
   * 
   * @returns 返回一个内部 唯一 的订单号
   */
  async creadOrder(id) {
    var timer = new Date()
    return timer.getFullYear() + "" + ((timer.getMonth() + 1).toString().padStart(2, "0")) + timer.getDate() + timer.getMilliseconds().toString().padStart(3, "0") + ((timer.getHours() + 6).toString().padStart(2, "0")) + timer.getSeconds().toString().padStart(2, "0") + timer.getMinutes() + (id + parseInt(Math.random() * 1000 + "")) + ""

  }


  async createSignv3(obj: { method: string, url: string, timeStamp: any, ranstr: any, body: any }) {

    var str = `${obj.method}\n${obj.url}\n${obj.timeStamp}\n${obj.ranstr}\n${JSON.stringify(obj.body)}\n`
    var signture = await crypto.createSign('RSA-SHA256')
    signture.update(str)
    var signValue = await signture.sign(fs.readFileSync(path.resolve(__dirname, "../cert/apiclient_key.pem")), 'base64')

    return signValue
  }

  async getNonceStr(len) {
    let str = "0123456789abcdefghijklmnopqrstuvwxyz";
    let newStr = "";
    for (let i = 0; i < len; i++) {
      let index = Math.floor(Math.random() * str.length);
      newStr += str[index];
    }
    return newStr;
  }

  async createSignv3str(str) {
    console.log("加密内容", str);

    var signture = await crypto.createSign('RSA-SHA256')
    signture.update(str)
    var signValue = signture.sign(fs.readFileSync(path.resolve(__dirname, "../cert/apiclient_key.pem")), 'base64')

    return signValue
  }


  async notify(option: any) {
    // ctx
    console.log(this.ctx);
    console.log("参数");
    console.log(option);

    // option = {
    //   id: '31d2d4d5-1d6f-5125-bbbb-0efebed41072',
    //   create_time: '2023-06-06T09:27:24+08:00',
    //   resource_type: 'encrypt-resource',
    //   event_type: 'TRANSACTION.SUCCESS',
    //   summary: '支付成功',
    //   resource: {
    //     original_type: 'transaction',
    //     algorithm: 'AEAD_AES_256_GCM',
    //     ciphertext: 'DDLZPpTxVVJK0Y1lLRgU88qcQisW4k1p7AUCrPt9zs5Sn7rzMEgddg7cZ38sdAfIDusJPuzxBmEMrhXJlz6Wi5Z3+ebj+FvvAv1vg4yfoG/gaimCzwPY7semkQW5z6cQl8bo4c5xae35sw3EDt5Tr9inHm6aTBk096PVmB01lQmGAdjQh+UTt7BXHIitcPgWUxQskA33qiljeV2oTV+SWTd1sPGd3dmXWtuspDnsSq6oF+/j5IxmlqfrykteaH8AMCTCJ4cTHDD/Lfn0EajyirTfZGRbRuXf4qrFEW1uR2mM9ZU185Y+rjx1TAhXq8WzyYaqldX2D8hyDsmOX/Uk83XzEZ6I4H+l32C+8zUL1LAwhMXHvH+M8g76vuc0JX0fvKl77Xb8JzJAj9PpZnNQSW6WqTkvfOMnttDl5rbkqmLvZSzICKpgOxEvWeTBwRHlLGO9Cy4mLEStXx349RLrcyDvWcRFUxxozxlmbNWqfkxz90EfHOIP7ehKvtLxzuJFW4IYn2c1VJOAaFnr2LS/ntl9aC+19DKozcHDzqh0kjPGZOkFXKSJCxpWzdl5mEk5Jkc=',
    //     associated_data: 'transaction',
    //     nonce: 'ddJrf7pA5133'
    //   }
    // }

    //  ciphertext  一个加密的密文  记录我们付款的订单号 

    // let { ciphertext, nonce, associated_data } = option.resource
    // let key = privacy.pay_api_key
    // try {
      // let obj = await this.veryV3(ciphertext, key, nonce, associated_data)
      // //                 解密的函数
      // console.log(obj);
      /* 
      {
        mchid: '1602299342',
        appid: 'wx462bc4b528a13c96',
        out_trade_no: '2023066594151127663',   //内部的订单编号
        transaction_id: '4200001874202306067786564642', // 微信内部的对应订单编号
        trade_type: 'JSAPI',
        trade_state: 'SUCCESS',
        trade_state_desc: '支付成功',
        bank_type: 'OTHERS',
        attach: '',
        payer: { openid: 'o01r05CtNMAI4RxsY4FHu454xpWg' },
        amount: { total: 3, payer_total: 3, currency: 'CNY', payer_currency: 'CNY' }
      }
      */


      // if (obj.trade_state == "SUCCESS") {
      //   //   修改数据库订单状态
      //   await this.OrderModel.createQueryBuilder().update().set({ status: 1 }).where({ orderid: obj.out_trade_no }).execute();
      // }

      //  订单信息
    // } catch (err) {
    //   console.log("解析出错", err)
    // }
    return {
      code: 200,
      message: "成功"
    }
  }


  /**
 * 解密
 *
 * @param {string} cipherText 文本
 * @param {string} key V3密钥
 * @param {string} iv 偏移量（nonce）
 * @param {string} add associated_data  
 * @returns { id: '4200000421201910275143005680',appid: 'wx459f9b30b8d5b725',mchid: '105242849',out_trade_no: '1572159737154',payer: { openid: 'oDBryjphoxK-jG0UWcqEu4dCavZI' },amount:{ total: 10,currency: 'HKD',payer_total: 9,payer_currency: 'CNY',exchange_rate: { type: 'SETTLEMENT_RATE', rate: 100000000 } },trade_type: 'JSAPI',trade_state: 'SUCCESS',trade_state_desc: '支付成功',bank_type: 'CFT',success_time: '2019-10-27T15:02:26+08:00' }
 */
  async veryV3(cipherText, key: string, iv: string, add: string) {
    cipherText = Buffer.from(cipherText, 'base64');
    let authTag = cipherText.slice(cipherText.length - 16);
    let data = cipherText.slice(0, cipherText.length - 16);
    let decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    decipher.setAuthTag(authTag);
    decipher.setAAD(Buffer.from(add));
    let decoded = decipher.update(data, null, 'utf8');
    decipher.final();
    return JSON.parse(decoded);
  }
  async decryptToString_AES(options) {
    const { associated_data, ciphertext, key, nonce } = options;
    const ciphertextBuffer = Buffer.from(ciphertext, "base64");
    const authTag = ciphertextBuffer.slice(ciphertextBuffer.length - 16);
    const data = ciphertextBuffer.slice(0, ciphertextBuffer.length - 16);
    const decipherIv = crypto.createDecipheriv("aes-256-gcm", key, nonce);
    decipherIv.setAuthTag(authTag);
    decipherIv.setAAD(Buffer.from(associated_data));
    const decryptBuf = decipherIv.update(data);
    decipherIv.final();
    return decryptBuf.toString("utf8");
  }

  async decode(params) {
    const AUTH_KEY_LENGTH = 16;
    // ciphertext = 密文，associated_data = 填充内容， nonce = 位移
    const { ciphertext, associated_data, nonce } = params;
    // 密钥
    const key_bytes = Buffer.from(privacy.pay_api_key, 'utf8');
    // 位移
    const nonce_bytes = Buffer.from(nonce, 'utf8');
    // 填充内容
    const associated_data_bytes = Buffer.from(associated_data, 'utf8');
    // 密文Buffer
    const ciphertext_bytes = Buffer.from(ciphertext, 'base64');
    // 计算减去16位长度
    const cipherdata_length = ciphertext_bytes.length - AUTH_KEY_LENGTH;
    // upodata
    const cipherdata_bytes = ciphertext_bytes.slice(0, cipherdata_length);
    // tag
    const auth_tag_bytes = ciphertext_bytes.slice(cipherdata_length, ciphertext_bytes.length);
    const decipher = crypto.createDecipheriv(
      'aes-256-gcm', key_bytes, nonce_bytes
    );
    decipher.setAuthTag(auth_tag_bytes);
    decipher.setAAD(Buffer.from(associated_data_bytes));


    const output = Buffer.concat([
      decipher.update(cipherdata_bytes),
      decipher.final(),
    ]);
    return output;
  }


}
